import { createApp } from 'vue'
import TooltipVue, { TooltipPositionArr } from './TTooltips.vue'
import { getZIndex } from '../../utils/utils'
import type { TooltipProps, TooltipPosition } from './TTooltips.vue'
import type { ComponentPublicInstance, DirectiveBinding, Directive } from 'vue'

// 提示条显示距离位置
const tooltipMargin = 10

export type VTooltip = Directive<TooltipEl, string>

interface TooltipInstance extends ComponentPublicInstance {
  setTitleValue?: (val: string | number) => void
}

interface TooltipEl extends HTMLElement {
  tooltipInstance: TooltipInstance | undefined
  tooltipTimer: ReturnType<typeof setTimeout> | null
}

// 提示语位置
const tooltipPosition = (position: TooltipPosition, tooltipDom: HTMLElement, mountDomRect: DOMRect): void => {
  tooltipDom.style.zIndex = String(getZIndex())
  switch (position) {
    case 'topLeft':
      tooltipDom.style.left = `${mountDomRect.left}rem`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}rem`
      break
    case 'top':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}rem`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}rem`
      break
    case 'topRight':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width}rem`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}rem`
      break
    case 'rightTop':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top}rem`
      break
    case 'right':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height / 2}rem`
      break
    case 'rightBottom':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width + tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height}rem`
      break
    case 'leftTop':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top}rem`
      break
    case 'left':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height / 2}rem`
      break
    case 'leftBottom':
      tooltipDom.style.left = `${mountDomRect.left - tooltipMargin}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height}rem`
      break
    case 'bottomLeft':
      tooltipDom.style.left = `${mountDomRect.left}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}rem`
      break
    case 'bottom':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}rem`
      break
    case 'bottomRight':
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width}rem`
      tooltipDom.style.top = `${mountDomRect.top + mountDomRect.height + tooltipMargin}rem`
      break
    default:
      tooltipDom.style.left = `${mountDomRect.left + mountDomRect.width / 2}rem`
      tooltipDom.style.top = `${mountDomRect.top - tooltipMargin}rem`
      break
  }
}
// 初始化提示
const initTooltip = (el: TooltipEl, binding: DirectiveBinding, isChange = true): HTMLElement => {
  const maxWidth = el.getAttribute('ttt-tooltip-width')
  const color = el.getAttribute('ttt-tooltip-color')
  const plain = el.getAttribute('ttt-tooltip-plain')
  const mountDomRect = el.getBoundingClientRect()
  const modifiers = Object.keys(binding.modifiers)
  const position = modifiers.find((item) => binding.modifiers[item])
  const isPosition = position && TooltipPositionArr.includes(position)
  if (!el.tooltipInstance) {
    const dom: HTMLElement = document.createElement('div')
    const prop: Partial<TooltipProps> = {
      title: binding.value,
      position: isPosition ? (position as TooltipPosition) : 'top',
      width: maxWidth || 220,
      color: color || '',
      plain: plain !== null
    }
    el.tooltipInstance = createApp(TooltipVue, prop).mount(dom)
  } else {
    if (isChange && el.tooltipInstance?.setTitleValue) el.tooltipInstance.setTitleValue(binding.value)
    el.tooltipInstance.$el.style.display = 'block'
  }
  const tooltipDom: HTMLElement = el.tooltipInstance.$el
  tooltipPosition(position as TooltipPosition, tooltipDom, mountDomRect)
  return tooltipDom
}
// 移除提示
const removeTooltip = (el: TooltipEl): void => {
  if (!el.tooltipInstance) return
  el.tooltipInstance.$el.remove()
}
// 鼠标移入
const mouseenter = (el: TooltipEl, binding: DirectiveBinding): (() => void) => {
  return () => {
    const time = el.getAttribute('ttt-tooltip-time') || 0
    el.tooltipTimer && clearTimeout(el.tooltipTimer)
    el.tooltipTimer = setTimeout(() => {
      const textOver = el.getAttribute('ttt-tooltip-text-over')
      if (textOver !== null && el.scrollWidth <= el.clientWidth) return
      document.body.appendChild(initTooltip(el, binding, false))
    }, Number(time))
  }
}
// 鼠标移出
const mouseleave = (el: TooltipEl): (() => void) => {
  return () => {
    el.tooltipTimer && clearTimeout(el.tooltipTimer)
    if (el.tooltipInstance?.$el) el.tooltipInstance.$el.style.display = 'none'
  }
}

const vTooltip: VTooltip = {
  mounted(el: TooltipEl, binding: DirectiveBinding) {
    const textOver = el.getAttribute('ttt-tooltip-text-over')
    if (textOver !== null) {
      el.style.whiteSpace = 'nowrap'
      el.style.textOverflow = 'ellipsis'
      el.style.overflow = 'hidden'
    }
    el.addEventListener('mouseenter', mouseenter(el, binding))
    el.addEventListener('mouseleave', mouseleave(el))
  },
  updated(el: TooltipEl, binding: DirectiveBinding) {
    if (el.tooltipInstance?.setTitleValue) el.tooltipInstance.setTitleValue(binding.value)
  },
  beforeUnmount(el: TooltipEl, binding: DirectiveBinding) {
    removeTooltip(el)
    el.removeEventListener('mouseenter', mouseenter(el, binding))
    el.removeEventListener('mouseleave', mouseleave(el))
  }
}


export default vTooltip